#include "stdafx.h"
#include "Export.h"
#include "ImageSplitter.h"
#include "WordProxy.h"
#include "Utils.h"
#include "FileNameUtils.h"
#include <iostream>
#include <iomanip>
#include <memory>

#include "boost/format.hpp"

using namespace boost;
using boost::io::group;

typedef basic_format<TCHAR> tformat;

bool DoExport(const tstring& FileName, bool MultipleFiles, const CProgramOptions& Options)
{
	CImageSplitter Splitter;	
	if( !CBaseExport::LoadAndResizeImage(FileName, Options, Splitter) )
		return false ;
	
	ImageType_t OutputImageType = Options.GetOutputImageType(FileName);
	if( OutputImageType != imtUnknown )
		Splitter.SetOutputImageType(OutputImageType);

	tstring Prefix, Suffix;
	Options.GetOutputFileNamePattern(FileName, Prefix, Suffix);
	
	tstring OutuputFileName = Prefix;
	// Add infix if necessary
	if( Options.NeedAdditionalInfix() && MultipleFiles)
	{
		tstring Infix;
		ExtractFileName_WithoutPath_WithoutExt(FileName, Infix);
		Prefix += _T("_");
		Prefix += Infix;
	}
	if( Options.GetExportType() == exp2Word )
	{
		return CWordExport::ExportToWord(Splitter, Prefix + Suffix ); // overrite files even if it exists
	}
	else if (Options.GetExportType() == exp2File)
	{
		SIZE PieceSize = {0};
		Options.GetPieceSize( PieceSize );
		return CFileExport::ExportToFile(Splitter, Prefix, Suffix, PieceSize );
	}
	else
		_ASSERT( FALSE );
	return false;
}

/////////////////////// CBaseExport ///////////////////////

#pragma warning(push)
#pragma warning(disable: 4244)
void CBaseExport::ResizeCompletion(const SIZE& OriginalSize, SIZE& NewSize)
{
	_ASSERT( (NewSize.cx != 0) || (NewSize.cy != 0) );
	_ASSERT( (OriginalSize.cx != 0) && (OriginalSize.cy != 0) );
	if( NewSize.cx == 0 )
	{
		NewSize.cx = OriginalSize.cx * ((float)NewSize.cy / (float)OriginalSize.cy);
		return;
	}
	if( NewSize.cy == 0 )
	{
		NewSize.cy = OriginalSize.cy * ((float)NewSize.cx / (float)OriginalSize.cx);
		return;
	}
}

void CBaseExport::PieceSizeCompletion(const SIZE& OriginalSize, SIZE& PieceSize)
{
	_ASSERT( (PieceSize.cx != 0) || (PieceSize.cy != 0) );
	_ASSERT( (OriginalSize.cx != 0) && (OriginalSize.cy != 0) );
	if( PieceSize.cx == 0 )
	{
		PieceSize.cx = OriginalSize.cx * ((float)PieceSize.cy / (float)OriginalSize.cy);
		return;
	}
	if( PieceSize.cy == 0 )
	{
		PieceSize.cy = OriginalSize.cy * ((float)PieceSize.cx / (float)OriginalSize.cx);
		return;
	}
}
#pragma warning(pop)

const wchar_t* cUnableToOpenImage = L"Unable to open image.";
const wchar_t* cLoadingImageMsg = L"Wait ...";

bool CBaseExport::LoadAndResizeImage(const tstring& InputFileName, const CProgramOptions& Options, CImageSplitter& Splitter)
{
	try
	{
		std::wcout << cLoadingImageMsg << std::endl;
		Splitter.OpenImage(InputFileName.c_str() );
		Splitter.SetJPEGQuailty( Options.GetCompressQuality() );
		if( Options.NeedResize() )
		{
			SIZE NewSize = {0};
			SIZE OriginalSize = {0};
			Options.GetNewSize(NewSize);
			OriginalSize.cx = Splitter.GetImageWidthInPixels();
			OriginalSize.cy = Splitter.GetImageHeightInPixels();

			CBaseExport::ResizeCompletion(OriginalSize, NewSize);
			Splitter.ResizeImage(NewSize.cx, NewSize.cy);
		}
	}
	catch(EUnableToOpenImage& )
	{
		std::wcout << cUnableToOpenImage << std::endl << std::endl;
		return false;
	}
	return true;
}

bool CBaseExport::CheckWriteAccessToFile(const tstring& Path)
{
	bool bAlredyExists = FileExists( Path );
	HANDLE hFile = CreateFile(Path.c_str(), FILE_WRITE_DATA, 0, NULL, CREATE_ALWAYS, 0, NULL);
	
	if ( INVALID_HANDLE_VALUE == hFile )
			return false;

	CloseHandle( hFile );
	
	if( !bAlredyExists )
		DeleteFile( Path.c_str() );	
	return true;
}

size_t CBaseExport::GetOptimalFillWidth(size_t PieceCount)
{
	if( PieceCount <= 1 )
		return 0; // Show that 
	return  (size_t) (log((double)PieceCount ) / log((double)10) + 1);
}

const wchar_t* cUnableToResolvePath = L"Unable to get full path from ";
const wchar_t* cUnableToCreatePath = L"Unable to create path ";
const wchar_t* cWrongPathType = L"Wrong path type (directory/file) ";
const wchar_t* cAccessDenied = L"Access denied to ";

void CBaseExport::ProcessCheckPathError(const ECheckPathError& Error)
{
	tstring HelpString;
	Error.GetHelpString(HelpString);

	switch( Error.GetContext() )
	{
	case cpeUnableToResolvePath: 
		std::wcout << cUnableToResolvePath << HelpString << std::endl;
		break;
	case cpeUnableToCreatePath:
		std::wcout << cUnableToCreatePath << HelpString << std::endl;
		break;
	case cpeWrongPathType:
		std::wcout << cWrongPathType << HelpString << std::endl;
		break;
	case cpeAccessDenied:
		std::wcout << cAccessDenied << HelpString << std::endl;
	}
}

const wchar_t* cUnableToInitOLEMsg = L"OleInitialize failed.";
const wchar_t* cWordInitializationFailure = L"Initialization of MSWord failed.";
const wchar_t* cInvocationError = L"Error on accessing to method or property.";
const wchar_t* cUnableToSavePiece = L"Unable to save piece.";

void CWordExport::ExportIthPieceToWord(CImageSplitter& Splitter, CWordProxy& Word, int i)
{
	tstring TempFileName;
	if( !GetTempFileName(TempFileName) )
		throw EUnableToSaveImage();

	std::wcout << L"Getting " << i << L" piece ... " << std::endl; // Temp
	Splitter.GetPiece(i, TempFileName.c_str() );
	tstring PieceFileName;
	if( !ChangeFileExtAndRename(TempFileName, _T("jpg"), PieceFileName) )
		throw EUnableToSaveImage();
	std::wcout << L"Exporting " << i << L" piece to word ... " << std::endl;
	Word.PastePicture(PieceFileName.c_str() );
	DeleteFile(PieceFileName.c_str() );
}

bool CWordExport::ExportToWord(CImageSplitter& Splitter, const tstring& OutputFile)
{
	bool bResult = false;
	CWordProxy Word;
	if( OleInitialize(NULL) != S_OK )
	{
		std::cout << cUnableToInitOLEMsg << std::endl;
		return bResult;
	}
	try
	{
		Word.Initialize();
		Word.NewDocument();
		DOCUMENT_PAGE_SIZE PageSize = {0};
		Word.GetPageSizeInPt(PageSize);
		DOCUMENT_MARGINS Margins = {0};
		Word.GetMarginsInPt(Margins);
		LONG ActualWidth = Word.PointsToPixels(PageSize.Width - (Margins.Left + Margins.Right), false);
		LONG ActualHeight = Word.PointsToPixels(PageSize.Height - (Margins.Bottom + Margins.Top) - 4 , true);
		Splitter.SplitImage(ActualWidth, ActualHeight);

		ExportIthPieceToWord(Splitter, Word, Splitter.GetPieceCount());
		for( int i = Splitter.GetPieceCount() - 1; i > 0; i--)
		{	
			SIZE PieceSize = {0};
			Splitter.GetPieceSize(i, PieceSize);
			if( PieceSize.cy < ActualHeight ) // TODO: test it
				Word.PageBreakAndUP();

			ExportIthPieceToWord(Splitter, Word, i);		
		}
		Word.SaveAs(OutputFile.c_str());
		bResult = true;
	}
	catch(EIinitializationError)
	{
		std::cout << cWordInitializationFailure << std::endl;
	}
	catch(EInvocationError)
	{
		std::cout << cInvocationError << std::endl;
	}
	catch(EUnableToSaveImage)
	{
		std::cout << cUnableToSavePiece << std::endl;
	}
	Word.Finalize();
	OleUninitialize();
	return bResult;
}

bool CFileExport::ExportToFile(CImageSplitter &Splitter, const tstring& OutFilePrefix, const tstring& OutFileSuffix, const SIZE& RawPieceSize)
//void CFileExport::ExportToFile(CImageSplitter &Splitter, const tstring& OutputFile, const SIZE& RawPieceSize, const tstring& OutputFilePattern, int MaxOutputFileLen)
{
	bool bResult = false;
	if( (RawPieceSize.cx != 0) || (RawPieceSize.cy != 0) )
	{
		SIZE PieceSize = RawPieceSize;
		SIZE ImageSize = {0};
		ImageSize.cy = Splitter.GetImageHeightInPixels();
		ImageSize.cx = Splitter.GetImageWidthInPixels();

		PieceSizeCompletion(ImageSize, PieceSize);
		Splitter.SplitImage(PieceSize.cx, PieceSize.cy);
		
		tstring Infix;
		try
		{
			size_t OptimalFillWidth = GetOptimalFillWidth( Splitter.GetPieceCount() );
			tformat FormatPattern( _T("_%1%") );
			if( OptimalFillWidth > 0 )
			{
				FormatPattern.exceptions( boost::io::no_error_bits );
				FormatPattern.modify_item(1, group(std::setfill(_T('0')), std::setw(OptimalFillWidth)) );
			}
			//
			tstring PathToCreate;
			ExtractFilePath(OutFilePrefix, PathToCreate);
			if( !ForceDirectories(PathToCreate) )
				throw ECheckPathError(cpeUnableToCreatePath, PathToCreate);
			//
			for( int i = 1; i<=Splitter.GetPieceCount(); i++)
			{
				tstring IthPieceName;
				if( OptimalFillWidth > 0 )
				{
					tstring Infix;
					Infix = str( FormatPattern % i );
					IthPieceName = OutFilePrefix + Infix + OutFileSuffix;
				}
				else
				{
					IthPieceName = OutFilePrefix + OutFileSuffix;
				}
				if( !CheckWriteAccessToFile(IthPieceName) )
					throw ECheckPathError(cpeAccessDenied, IthPieceName);
				std::wcout << L"Getting " << i << L" piece ... " << std::endl; // Temp
				Splitter.GetPiece(i, IthPieceName.c_str() );
				std::wcout << L" Saved. " << std::endl; // Temp
			}
			bResult = true;
		}
		catch(const EUnableToSaveImage& )
		{
			std::cout << cUnableToSavePiece << std::endl;
		}
		catch(const ECheckPathError& Error)
		{
			ProcessCheckPathError( Error );
		}
	}
	else
	{
		try
		{
			tstring OutFileName;
			OutFileName = OutFilePrefix + OutFileSuffix;
			//
			tstring PathToCreate;
			ExtractFilePath(OutFileName, PathToCreate);
			if( !ForceDirectories(PathToCreate) )
				throw ECheckPathError(cpeUnableToCreatePath, PathToCreate);
			//
			if( !CheckWriteAccessToFile(OutFileName) )
				throw ECheckPathError(cpeUnableToCreatePath, OutFileName);
			Splitter.GetPiece(0, OutFileName.c_str() );
			bResult = true;
		}
		catch(EUnableToSaveImage)
		{
			std::wcout << cUnableToSavePiece << std::endl;
		}
		catch(const ECheckPathError& Error)
		{
			ProcessCheckPathError( Error );
		}
	}
	return bResult;
}